package com.vxdata

object 函数_2_高阶函数 {
  def main(args: Array[String]): Unit = {
    // 定义一个函数
    def foo(): Unit = {
      println("foo...")
    }

    /**
     * TODO 函数作为值进行传递
     */
    // 调用 foo 函数，把返回值给变量 f
    val f = foo()
    println(f)

    // 在被调用函数 foo 后面加上 _，相当于把函数 foo 当成一个整体， 传递给变量 f1
    val f1 = foo _
    f1()

    // 如果明确变量类型，那么不使用下划线也可以将函数作为整体传递给变量
    def fInt(): Int = {
      println("foo...")
      1
    }

    var f2: () => Int = fInt


    /**
     * TODO 函数作为参数进行传递
     */


    // 定义一个函数，函数参数还是一个函数签名；f 表示函数名称;(Int,Int) 表示输入两个 Int 参数；Int 表示函数返回值
    def p(f: (Int, Int) => Int): Int = {
      f(2, 4)
    }

    // 定义一个函数，参数和返回值类型和 f1 的输入参数一致
    def add(a: Int, b: Int): Int = a + b
    // （3）将 add 函数作为参数传递给 p 函数，如果能够推断出来不是调用，_ 可以省略
    println(p(add))
    println(p(add _))


    /**
     * TODO 函数可以作为函数返回值返回
     */
    def r() = {
      def f2() = {
      }

      // f2作为返回值
      f2 _
    }

    val r1 = r()


    /**
     * TODO 匿名函数
     * 没有名字的函数就是匿名函数。
     * 格式： (x:Int)=>{函数体}
     */

    // 1. 定义一个函数：参数包含数据和逻辑函数
    def operation(arr: Array[Int], op: Int => Int) = {
      for (elem <- arr) yield op(elem)
    }

    // 2. 定义逻辑函数
    def op(ele: Int): Int = {
      ele + 1
    }

    // 3. 标准函数调用
    val arr = operation(Array(1, 2, 3, 4), op)

    // 4. 采用匿名函数
    val arr1 = operation(Array(1, 2, 3, 4), (ele: Int) => {
      ele + 1
    })

    // 4.1 参数的类型可以省略，会根据形参进行自动的推导
    val arr2 = operation(Array(1, 2, 3, 4), (ele) => {
      ele + 1
    })
    // 4.2 类型省略之后，发现只有一个参数，则圆括号可以省略；其他情况：没有参数和参数超过 1 的永远不能省略圆括号
    val arr3 = operation(Array(1, 2, 3, 4), ele => {
      ele + 1
    })
    // 4.3 匿名函数如果只有一行，则大括号也可以省略
    val arr4 = operation(Array(1, 2, 3, 4), ele => ele + 1)
    // 4.4 如果参数只出现一次，则参数省略且后面参数可以用_代替
    val arr5 = operation(Array(1, 2, 3, 4), _ + 1)

    // 5. 传递的函数有两个参数
    def calculator(a: Int, b: Int, op: (Int, Int) => Int): Int = {
      op(a, b)
    }

    // 5.1 标准写法
    calculator(2, 3, (x: Int, y: Int) => {
      x + y
    })
    // 5.2 如果只有一行，则大括号也可以省略
    calculator(2, 3, (x: Int, y: Int) => x + y)
    // 5.3 参数的类型可以省略，会根据形参进行自动的推导
    calculator(2, 3, (x, y) => x + y)
    // 5.4 如果参数只出现一次，则参数省略且后面参数可以用_代替
    calculator(2, 3, _ + _)
  }

}
